#include "mainwindow.h"

#include <QDateTime>
#include <QFile>
#include <QFileDialog>

#include "b_mod_utc.h"
#include "uartclass.h"
#include "ui_mainwindow.h"
#include "algo_crc.h"

uartClass   uartModule;
MainWindow *tmpClass;

extern "C" int Dispatch(bProtoID_t id, uint8_t cmd, uint8_t *param, bProtoLen_t param_len);

/**
 * \brief 协议的命令号
 */
#define CMD_TEST (0x1)
#define CMD_SET_TIME (0x2)
#define CMD_FW_INFO (0x3)
#define CMD_UPGRADE_DATA (0x4)
#define CMD_UPGRADE_RESULT (0x5)
#define CMD_TRANS_FILE (0x6)
/**
 * \brief 协议参数对应的数据结构
 */
#pragma pack(1)

typedef struct
{
    uint32_t file_size;
    uint32_t file_crc;
    char     file_name[64];
} bUpgradeStart_t;

typedef struct
{
    uint16_t number;
} bUpgradeReqData_t;

typedef struct
{
    uint16_t number;
    uint8_t  data[512];
} bUpgradeTransData_t;

typedef struct
{
    uint32_t size;
    uint32_t f_crc32;
    uint32_t dev_no;
    uint32_t offset;
} bProtoTransFileParam_t;

#pragma pack()

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    quartTimer = new QTimer(this);
    quartTimer->setSingleShot(true);
    connect(quartTimer, SIGNAL(timeout()), this, SLOT(TimerTimeout()));
    quartTimer->start(100);
    ui->comboBox->addItems(uartModule.uartComAvailable);

    protocol_n = bProtocolRegist(0x1314, Dispatch);
    tmpClass   = this;
    bin_file_data.clear();

    ui->dev_no_lineEdit->setText("0");
    ui->file_offset_lineEdit->setText("0");
}

MainWindow::~MainWindow()
{
    delete ui;
}

/**
 * \brief 打开串口和关闭串口
 */
void MainWindow::on_comBUTTON_clicked()
{
    uint32_t bps = 115200;
    if (uartModule.uartGetOpenStatus())
    {
        uartModule.uartClosePort();
        ui->comboBox->setEnabled(true);
        ui->comBUTTON->setText("打开串口");
    }
    else
    {
        if (ui->checkBoxBPS->isChecked())
        {
            bps = 9600;
        }
        if (uartModule.uartOpenPort(ui->comboBox->currentText(), bps))
        {
            ui->comboBox->setEnabled(false);
            ui->comBUTTON->setText("关闭串口");
        }
    }
}

/**
 * \brief 刷新串口号
 */
void MainWindow::on_refreshButton_clicked()
{
    ui->comboBox->clear();
    uartModule.uartRefreshCOM();
    ui->comboBox->addItems(uartModule.uartComAvailable);
}

/**
 * \brief 定时器的回调，用于定时查询串口是否接收到数据
 */
void MainWindow::TimerTimeout()
{
    uint32_t len;
    uint8_t  tmp_buf[10240];
    len          = uartModule.uartReadBuff(tmp_buf);
    tmp_buf[len] = '\0';
    if (len > 0)
    {
        /**
         * 如果勾选了加密传输，则先解密
         */
        if (ui->encryptBox->isChecked() == true)
        {
            _bProtocolDecrypt(tmp_buf, len);
        }
        /**
         * 将数据喂给协议解析模块进行解析，解析完成后调用分发函数
         */
        if (0 > bProtocolParse(protocol_n, tmp_buf, len))
        {
            QString str = QString::fromStdString((char *)tmp_buf);
            ui->recTEXT->append(str);
        }
    }
    quartTimer->start(100);
}

QString hexToString(unsigned char *in, int len)
{
    int           i;
    unsigned char inChar, hi, lo;
    QString       s;

    for (i = 0; i < len; i++)
    {
        inChar = in[i];

        hi = (inChar & 0xF0) >> 4;
        if (hi > 9)
            hi = 'A' + (hi - 0x0A);
        else
            hi += 0x30;
        s.append((QChar)hi);

        lo = inChar & 0x0F;
        if (lo > 9)
            lo = 'A' + (lo - 0x0A);
        else
            lo += 0x30;
        s.append((QChar)lo);

        s.append((QChar)0x20);
    }
    return s;
}

/**
 * \brief 接收文本框内显示接收的指令信息
 */
int MainWindow::ShowLog(bProtoID_t id, uint8_t cmd, uint8_t *param, bProtoLen_t param_len)
{
    QString str = "r->";
    str         = str + "id: " + QString::number(id);
    str         = str + "cmd: " + QString::number(cmd);
    str         = str + "param: " + hexToString(param, param_len);
    ui->recTEXT->append(str);
    return 0;
}

/**
 * \brief 收到数据请求后，通过此函数发送数据
 */
int MainWindow::TransData(uint16_t num)
{
    bUpgradeTransData_t dat;
    uint8_t             table[1024];
    int                 len   = 0;
    uint32_t            index = 0;
    index                     = num * 512;
    if (index >= bin_file_len)
    {
        return -1;
    }
    memset(&dat, 0, sizeof(bUpgradeTransData_t));
    dat.number = num;
    memcpy(dat.data, bin_file_data.data() + index,
           ((bin_file_len - index) >= 512) ? 512 : (bin_file_len - index));
    len = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_UPGRADE_DATA, (uint8_t *)&dat,
                        sizeof(bUpgradeTransData_t), table);
    ui->recTEXT->append(hexToString(table, len));
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
    ui->transprogressBar->setValue((index * 100 / bin_file_len));
}

/**
 * \brief 处理设备上报的结果
 */
int MainWindow::AckResult(uint8_t result)
{
    ui->transprogressBar->setValue(100);
    ui->recTEXT->append(QString("result:%1").arg(result));
    if(result == 0)
    {
        ui->recTEXT->append("success");
    }
    else
    {
        ui->recTEXT->append("fail");
    }
    return 0;
}

/**
 * \brief 返回固件信息
 */
int MainWindow::AckFwInfo()
{
    bUpgradeStart_t info;
    uint8_t         table[128];
    int             len = 0;
    char           *pfwname;
    memset(&info, 0, sizeof(bUpgradeStart_t));
    if (ui->filenameLable->text().isEmpty())
    {
        return -1;
    }
    if (ui->FwnamelineEdit->text().isEmpty())
    {
        return -1;
    }
    pfwname        = ui->FwnamelineEdit->text().toLatin1().data();
    info.file_size = bin_file_len;
    info.file_crc  = bin_file_crc;
    memcpy(info.file_name, pfwname, (strlen(pfwname) > 63) ? 63 : strlen(pfwname));
    len = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_FW_INFO, (uint8_t *)&info,
                        sizeof(bUpgradeStart_t), table);
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
    return 0;
}

/**
 * \brief 协议解析的分发函数，在此函数，根据指令执行对应的操作
 */
int Dispatch(bProtoID_t id, uint8_t cmd, uint8_t *param, bProtoLen_t param_len)
{
    uint8_t  result = 0;
    uint16_t num    = 0;
    tmpClass->ShowLog(id, cmd, param, param_len);
    switch (cmd)
    {
        case CMD_UPGRADE_DATA:
        {
            if (param_len < sizeof(num) || param == NULL)
            {
                return -1;
            }
            num = *((uint16_t *)param);
            tmpClass->TransData(num);
        }
        break;
        case CMD_UPGRADE_RESULT:
        {
            if (param_len < sizeof(result) || param == NULL)
            {
                return -1;
            }
            result = *((uint8_t *)param);
            tmpClass->AckResult(result);
        }
        break;
        case CMD_FW_INFO:
        {
            //tmpClass->AckFwInfo();
        }
        break;
    }
    return 0;
}

void MainWindow::on_clearButton_clicked()
{
    ui->recTEXT->clear();
}

void MainWindow::on_pushButton_clicked()
{
    uint8_t table[128];
    int     len;
    uint8_t param[7] = "BabyOS";
    len              = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_TEST, param, 7, table);
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
}

void MainWindow::on_pushButtonSetTime_clicked()
{
    uint8_t         table[128];
    int             len = 0;
    uint32_t        utc = 0;
    bUTC_DateTime_t dt;

    dt.year  = QDateTime::currentDateTime().date().year();
    dt.month = QDateTime::currentDateTime().date().month();
    dt.day   = QDateTime::currentDateTime().date().day();

    dt.hour   = QDateTime::currentDateTime().time().hour();
    dt.minute = QDateTime::currentDateTime().time().minute();
    dt.second = QDateTime::currentDateTime().time().second();
    utc       = bStruct2UTC(dt);

    len = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_SET_TIME, (uint8_t *)&utc, 4, table);
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
}

void MainWindow::on_openfileBUTTON_clicked()
{
    QString file_name = QFileDialog::getOpenFileName(this, tr("open file"), " ", tr("bin(*.bin)"));
    ui->filenameLable->setText(file_name);
    QFile bin_file(file_name);
    if (!bin_file.open(QIODevice::ReadOnly))
    {
        return;
    }
    bin_file_data = bin_file.readAll();
    bin_file.close();
    bin_file_len     = bin_file_data.length();
    bin_file_crc     = crc_calculate(ALGO_CRC32, (uint8_t *)bin_file_data.data(), bin_file_data.length());
    QString bin_info = QString("len:%1 crc32:%2").arg(bin_file_len).arg(bin_file_crc, 0, 16);
    ui->recTEXT->append(bin_info);
}

void MainWindow::on_upgradeBUTTON_clicked()
{
    bUpgradeStart_t info;
    uint8_t         table[128];
    int             len = 0;
    char           *pfwname;
    memset(&info, 0, sizeof(bUpgradeStart_t));
    if (ui->filenameLable->text().isEmpty())
    {
        ui->recTEXT->append("please select firmware file...");
        return;
    }
    if (ui->FwnamelineEdit->text().isEmpty())
    {
        ui->recTEXT->append("please set firmware name...");
        return;
    }
    pfwname        = ui->FwnamelineEdit->text().toLatin1().data();
    info.file_size = bin_file_len;
    info.file_crc  = bin_file_crc;
    memcpy(info.file_name, pfwname, (strlen(pfwname) > 63) ? 63 : strlen(pfwname));
    len = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_FW_INFO, (uint8_t *)&info,
                        sizeof(bUpgradeStart_t), table);
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
}

void MainWindow::on_transfileBUTTON_clicked()
{
    bProtoTransFileParam_t transfile;
    uint8_t         table[128];
    int             len = 0;
    memset(&transfile, 0, sizeof(bProtoTransFileParam_t));
    if (ui->filenameLable->text().isEmpty())
    {
        ui->recTEXT->append("please select firmware file...");
        return;
    }
    if (ui->file_offset_lineEdit->text().isEmpty())
    {
        ui->recTEXT->append("please set offset...");
        return;
    }
    if (ui->dev_no_lineEdit->text().isEmpty())
    {
        ui->recTEXT->append("please set dev_no...");
        return;
    }
    transfile.size = bin_file_len;
    transfile.f_crc32  = bin_file_crc;
    transfile.dev_no = ui->dev_no_lineEdit->text().toInt();
    transfile.offset = ui->file_offset_lineEdit->text().toInt();
    len = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_TRANS_FILE, (uint8_t *)&transfile,
                        sizeof(bProtoTransFileParam_t), table);
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
}

void MainWindow::on_stoptransfileBUTTON_clicked()
{
    bProtoTransFileParam_t transfile;
    uint8_t         table[128];
    int             len = 0;
    transfile.size = 0;
    transfile.f_crc32  = 0;
    transfile.dev_no = 0;
    transfile.offset = 0;
    len = bProtocolPack(protocol_n, 0xFFFFFFFF, CMD_TRANS_FILE, (uint8_t *)&transfile,
                        sizeof(bProtoTransFileParam_t), table);
    if (ui->encryptBox->isChecked() == true)
    {
        _bProtocolEncrypt(table, len);
    }
    uartModule.uartSendBuff(table, len);
}
